<template>
    <div class="upload-box">
        <el-upload
            ref="uploadImage"
            v-model:file-list="_fileList"
            :key="uploadKey"
            action="#"
            list-type="picture-card"
            :class="['upload', self_disabled ? 'disabled' : '', drag ? 'no-border' : '', shouldHideUpload ? 'hide-upload' : '']"
            :multiple="true"
            :disabled="self_disabled"
            :limit="limit"
            :http-request="handleHttpUpload"
            :before-upload="beforeUpload"
            :on-exceed="handleExceed"
            :on-success="uploadSuccess"
            :on-error="uploadError"
            :on-change="handleChange"
            :drag="drag"
            :accept="fileType.join(',')"
        >
            <div class="upload-empty">
                <slot name="empty">
                    <el-icon><Plus /></el-icon>
                </slot>
            </div>
            <template #file="{ file }">
                <img :src="file.url" class="upload-image img-cropper" :alt="file.name" :id="file.uid" />
                <div class="upload-handle" @click.stop>
                    <div v-if="!self_disabled" class="handle-icon" @click="openDialog(file)">
                        <el-icon><Crop /></el-icon>
                        <span>裁剪</span>
                    </div>
                    <div class="handle-icon" @click="handlePictureCardPreview(file)">
                        <el-icon><ZoomIn /></el-icon>
                        <span>查看</span>
                    </div>
                    <div v-if="!self_disabled" class="handle-icon" @click="handleRemove(file)">
                        <el-icon><Delete /></el-icon>
                        <span>删除</span>
                    </div>
                </div>
            </template>
        </el-upload>
        <div class="el-upload__tip">
            <slot name="tip"></slot>
        </div>
        <el-image-viewer
            v-if="imgViewVisible"
            :url-list="[viewImageUrl]"
            @close="imgViewVisible = false"
        />

        <!-- 图片裁剪弹窗 -->
        <el-dialog
            v-model="state.isShowDialog"
            :title="state.dialogTitle"
            :width="state.dialogWidth"
            destroy-on-close
            append-to-body
        >
            <div class="cropper-wrapper">
                <div class="cropper-left">
                    <img
                        ref="uploadImage"
                        :src="imageSrc"
                        alt="裁剪图片"
                        class="cropper-image"
                        v-if="imageSrc"
                    />
                </div>
                <div class="cropper-right" v-if="croppedImageSrc">
                    <div class="preview-title">预览</div>
                    <div class="preview-image">
                        <img :src="croppedImageSrc" alt="预览图片" />
                    </div>
                </div>
            </div>
            <template #footer>
                <div class="dialog-footer">
                    <el-button @click="changeScale(-0.1)">缩小</el-button>
                    <el-button @click="changeScale(0.1)">放大</el-button>
                    <el-button @click="rotateLeft(-90)">左转</el-button>
                    <el-button @click="rotateRight(90)">右转</el-button>
                    <el-button type="primary" @click="cropImage">裁剪</el-button>
                    <el-button type="success" @click="uploadCroppedImage">上传</el-button>
                    <el-button @click="closeDialog">取消</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts" name="UploadImgs">
import { ref, computed, inject, watch, reactive, nextTick, onMounted, onUnmounted } from 'vue';
import { Crop, Delete, Plus, ZoomIn } from '@element-plus/icons-vue';
import { uploadImg } from '/@/views/system/upload';
import {
    UploadProps,
    UploadFile,
    UploadUserFile,
    UploadRequestOptions,
    ElMessage,
    ElNotification,
} from 'element-plus';
import { formContextKey, formItemContextKey } from 'element-plus';

interface UploadFileProps {
    fileList: UploadUserFile[];
    drag?: boolean; // 是否支持拖拽上传 ==> 非必传（默认为 true）
    disabled?: boolean; // 是否禁用上传组件 ==> 非必传（默认为 false）
    limit?: number; // 最大图片上传数 ==> 非必传（默认为 5张）
    fileSize?: number; // 图片大小限制 ==> 非必传（默认为 5M）
    fileType?: any[]; // 图片类型限制 ==> 非必传（默认为 ["image/jpeg", "image/png", "image/gif"]）
    height?: string; // 组件高度 ==> 非必传（默认为 150px）
    width?: string; // 组件宽度 ==> 非必传（默认为 150px）
    borderRadius?: string; // 组件边框圆角 ==> 非必传（默认为 8px）
}
const props = defineProps({
    modelValue: {
        type: String,
        default: '',
    },
    fileList: {
        type: Array<UploadUserFile>,
        default: () => [],
    },
    drag: {
        type: Boolean,
        default: true,
    },
    disabled: {
        type: Boolean,
        default: false,
    },
    limit: {
        type: Number,
        default: 5,
    },
    fileSize: {
        type: Number,
        default: 5,
    },
    fileType: {
        type: Array,
        default: () => ['image/jpeg', 'image/png', 'image/gif'],
    },
    height: {
        type: String,
        default: '150px',
    },
    width: {
        type: String,
        default: '150px',
    },
    borderRadius: {
        type: String,
        default: '8px',
    },
    cropperWidth: {
        type: Number,
        default: 400,
    },
    cropperHeight: {
        type: Number,
        default: 400,
    },
});
const emit = defineEmits<{
    'update:modelValue': [value: string];
    success: [value: { urls: string; fileList: UploadUserFile[] } | string];
}>();
// 获取 el-form 组件上下文
const formContext = inject(formContextKey, void 0);
// 获取 el-form-item 组件上下文
const formItemContext = inject(formItemContextKey, void 0);
// 判断是否禁用上传和删除
const self_disabled = computed(() => {
    const disabled = props.disabled || formContext?.disabled;
    console.log('Self disabled:', disabled, 'props.disabled:', props.disabled, 'formContext?.disabled:', formContext?.disabled);
    return disabled;
});

// 判断是否应该隐藏上传按钮
const shouldHideUpload = computed(() => {
    // 当文件数量达到限制时隐藏上传按钮
    return _fileList.value.length >= props.limit;
});

// 默认回显图片
const _fileList = ref<UploadUserFile[]>([]);
const uploadKey = ref(Date.now()); // 用于强制重新渲染组件

// 监听文件列表变化，确保数据同步
watch(
    () => _fileList.value,
    (newVal) => {
        // 可以在这里添加调试日志
        // console.log('File list updated:', newVal);
    },
    { deep: true }
);

// 在组件挂载后执行一些逻辑
onMounted(() => {
    console.log('Component mounted, modelValue:', props.modelValue);
    // 清空文件列表，确保不残留上次的数据
    _fileList.value = [];

    if (props.modelValue == null || props.modelValue.length == 0) {
        console.log('No initial model value');
        return;
    }
    const arryIngs: Array<string> = props.modelValue.split(',');
    console.log('Split model value:', arryIngs);
    if (arryIngs.length > 0) {
        arryIngs.map((item: string) => {
            if (item.length > 0) {
                // 为每个文件生成唯一的UID
                const uid = Date.now() + Math.floor(Math.random() * 1000);
                _fileList.value.push({ url: item, name: item.length + '', uid });
            }
        });
    }
    console.log('Initialized file list:', _fileList.value);
});

// 在组件卸载前清理数据
onUnmounted(() => {
    _fileList.value = [];
});

watch(
    () => props.modelValue,
    (n: string) => {
        console.log('Model value changed:', n);
        // 只有当新的值与当前文件列表不匹配时才更新
        const currentUrls = _fileList.value.map((obj) => obj.url).join(',');
        console.log('Current URLs:', currentUrls);

        // 如果新值为空，则清空文件列表
        if (n == null || n.length == 0) {
            _fileList.value = [];
            // 更新key以强制重新渲染组件
            uploadKey.value = Date.now();
            console.log('New value is empty, cleared file list');
            return;
        }

        // 如果当前文件列表为空或者URL不匹配，则更新文件列表
        if (_fileList.value.length === 0 || n !== currentUrls) {
            _fileList.value = [];
            // 更新key以强制重新渲染组件
            uploadKey.value = Date.now();
            // eslint-disable-next-line no-console
            console.log('update:modelValue', n);
            const arryIngs: Array<string> = n.split(',');
            console.log('Split new value:', arryIngs);
            if (arryIngs.length > 0) {
                arryIngs.map((item: string) => {
                    if (item.length > 0) {
                        // 为每个文件生成唯一的UID
                        const uid = Date.now() + Math.floor(Math.random() * 1000);
                        _fileList.value.push({ url: item, name: item.length + '', uid });
                    }
                });
            }
        }
        console.log('Updated file list:', _fileList.value);
    },
    { flush: 'post' } // 确保在DOM更新后执行
);

/**
 * @description 文件上传之前判断
 * @param rawFile 选择的文件
 * */
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
    console.log('Before upload, file:', rawFile);
    const imgSize = rawFile.size / 1024 / 1024 < props.fileSize;
    const imgType = props.fileType.includes(rawFile.type);
    if (!imgType) {
        ElNotification({
            title: '温馨提示',
            message: '上传图片不符合所需的格式！',
            type: 'warning',
        });
        console.log('File type not allowed:', rawFile.type, 'Allowed types:', props.fileType);
    }
    if (!imgSize) {
        setTimeout(() => {
            ElNotification({
                title: '温馨提示',
                message: `上传图片大小不能超过 ${props.fileSize}M！`,
                type: 'warning',
            });
        }, 0);
        console.log('File size too large:', rawFile.size, 'Max size (MB):', props.fileSize);
    }
    const result = imgType && imgSize;
    console.log('Before upload result:', result);
    return result;
};

/**
 * @description 文件状态改变时的钩子
 * @param file 文件对象
 * @param fileList 文件列表
 * */
const handleChange: UploadProps['onChange'] = (file, fileList) => {
    // 确保文件列表正确更新
    _fileList.value = [...fileList];
    console.log('File changed:', file, 'File list:', fileList);
};

/**
 * @description 图片上传
 * @param options upload 所有配置项
 * */
const handleHttpUpload = async (options: UploadRequestOptions) => {
    console.log('Starting upload for file:', options.file);
    let formData = new FormData();
    formData.append('file', options.file);
    try {
        const { data } = await uploadImg(formData);
        console.log('Upload success, response:', data);
        options.onSuccess(data);
    } catch (error) {
        console.error('Upload error:', error);
        options.onError(error as any);
    }
};

/**
 * @description 图片上传成功
 * @param response 上传响应结果
 * @param uploadFile 上传的文件
 * */

const uploadSuccess = (response: { url: string } | undefined, uploadFile: UploadFile) => {
    console.log('Upload success callback, response:', response, 'uploadFile:', uploadFile);
    if (!response) {
        return;
    }
    // 确保每个文件都有独立的URL
    uploadFile.url = response.url;

    // 更新文件列表，确保所有文件都被正确跟踪
    const existingFileIndex = _fileList.value.findIndex(
        (file) => file.uid === uploadFile.uid
    );

    if (existingFileIndex !== -1) {
        // 如果文件已存在，更新它
        _fileList.value[existingFileIndex].url = response.url;
    } else {
        // 如果是新文件，添加到列表中
        _fileList.value.push({
            name: uploadFile.name,
            url: response.url,
            uid: uploadFile.uid
        });
    }

    // 发送更新后的完整URL列表
    const imgs = _fileList.value.map((obj) => obj.url).join(',');
    emit('update:modelValue', imgs);
    emit('success', {
        urls: imgs,
        fileList: [..._fileList.value]
    });

    // 调用 el-form 内部的校验方法（可自动校验）
    formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);

    ElNotification({
        title: '温馨提示',
        message: '图片上传成功！',
        type: 'success',
    });
};

/**
 * @description 删除图片
 * @param file 删除的文件
 * */
const handleRemove = (file: UploadFile) => {
    console.log('Removing file:', file);
    _fileList.value = _fileList.value.filter(
        (item) => item.uid !== file.uid
    );
    const imgs = _fileList.value.map((obj) => obj.url).join(',');
    emit('update:modelValue', imgs);
    console.log('After removal, file list:', _fileList.value);

    // 如果删除后文件列表为空，更新key以确保组件正确更新
    if (_fileList.value.length === 0) {
        uploadKey.value = Date.now();
    }
};

/**
 * @description 图片上传错误
 * */
const uploadError = (error: any, uploadFile: UploadFile, uploadFiles: UploadFile[]) => {
    console.error('Upload error:', error, 'File:', uploadFile, 'All files:', uploadFiles);
    ElNotification({
        title: '温馨提示',
        message: '图片上传失败，请您重新上传！',
        type: 'error',
    });
};

/**
 * @description 文件数超出
 * */
const handleExceed = () => {
    ElNotification({
        title: '温馨提示',
        message: `当前最多只能上传 ${props.limit} 张图片，请移除后上传！`,
        type: 'warning',
    });
    console.log('File limit exceeded, limit:', props.limit);
};

/**
 * @description 图片预览
 * @param file 预览的文件
 * */
const viewImageUrl = ref('');
const imgViewVisible = ref(false);
const handlePictureCardPreview: UploadProps['onPreview'] = (file) => {
    console.log('Previewing file:', file);
    viewImageUrl.value = file.url!;
    imgViewVisible.value = true;
};

// ====================== 裁剪 ======================
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
const croppedImageSrc = ref<string | null>(null);
const imageSrc = ref<string | undefined>(undefined);
const uploadImage = ref<HTMLImageElement | null>(null);
const uid = ref(0);
// 自定义值
const state = reactive({
    dialogWidth: '800px',
    dialogTitle: '图片裁剪上传',
    isShowDialog: false,
    cropperImg: '',
    cropperImgBase64: '',
    cropper: null as Cropper | null,
});

/**
 * 打开 裁剪图片
 * @param file
 */
const openDialog = async (file: UploadFile) => {
    if (!file.url) return;
    uid.value = file.uid;
    imageSrc.value = file.url.toString();
    croppedImageSrc.value = null;
    state.dialogWidth = '60%';
    state.isShowDialog = true;
    await nextTick();
    initCropper();
};

/**
 * 裁剪初始化
 */
const initCropper = () => {
    if (state.cropper) {
        state.cropper.destroy();
    }
    if (!uploadImage.value) return;
    state.dialogWidth = '60%';
    state.dialogTitle = '图片采集上传( ' + props.cropperWidth + ' x ' + props.cropperHeight + ' )';
    state.cropper = new Cropper(uploadImage.value, {
        aspectRatio: props.cropperWidth / props.cropperHeight, // 设置裁剪框的宽高比
        viewMode: 1, // 限制裁剪框不超过画布大小
    });
};


/**
 * 关闭 裁剪图片
 *
 */
const closeDialog = () => {
    state.isShowDialog = false;
    // 清空裁剪器
    if (state.cropper) {
        state.cropper.destroy();
        state.cropper = null;
    }
    // 清空图片源
    imageSrc.value = undefined;
    croppedImageSrc.value = null;
};



/**
 * 修改图片大小 正数为变大 负数变小
 * @param num
 */
const changeScale = (num: number) => {
    num = num || 1;
    if (state.cropper) {
        state.cropper.zoom(num);
    }
};
/**
 * 向左边旋转90度
 * @param num
 */
const rotateLeft = (num: number) => {
    num = num || 1;
    if (state.cropper) {
        state.cropper.rotate(num);
    }
};
/**
 * 向右边旋转90度
 * @param num
 */
const rotateRight = (num: number) => {
    num = num || 1;
    if (state.cropper) {
        state.cropper.rotate(num);
    }
};
/**
 * 裁剪图片
 */
const cropImage = () => {
    if (state.cropper) {
        const canvas = state.cropper.getCroppedCanvas({
            width: props.cropperWidth,
            height: props.cropperHeight,
        });
        // base64 格式
        croppedImageSrc.value = canvas.toDataURL('image/png') as any;
    }
};
/**
 * 上传图片
 */
const uploadCroppedImage = async () => {
    if (!croppedImageSrc.value) {
        ElMessage.error('请先裁剪图片');
        return;
    }
    try {
        const blob = dataURLToBlob(croppedImageSrc.value);
        const formData = new FormData();
        formData.append('file', blob, 'avatar.png'); // 注意这里的表单字段名应为'file'
        const response = await uploadImg(formData);
        if (response.code === 200) {
            // 使用更可靠的查找方式更新文件
            const fileIndex = _fileList.value.findIndex(item => item.uid === uid.value);
            if (fileIndex !== -1) {
                _fileList.value[fileIndex].url = response.data.url;
            }
            const imgs = _fileList.value.map((obj) => obj.url).join(',');
            emit('update:modelValue', imgs);
            ElMessage.success('上传成功');
            state.isShowDialog = false;
        } else {
            ElMessage.error(response.data.msg || '上传失败');
        }
    } catch (error) {
        ElMessage.error('上传失败');
    }
};

const dataURLToBlob = (dataURL: string): Blob => {
    const arr = dataURL.split(',');
    const match = arr[0].match(/:(.*?);/);
    const mime = match ? match[1] : '';
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], { type: mime });
};


</script>

<style scoped lang="scss">
.is-error {
    .upload {
        :deep(.el-upload--picture-card),
        :deep(.el-upload-dragger) {
            border: 1px dashed var(--el-color-danger) !important;
            &:hover {
                border-color: var(--el-color-primary) !important;
            }
        }
    }
}

// 裁剪功能样式
.cropper-wrapper {
    display: flex;

    .cropper-left {
        flex: 1;

        .cropper-image {
            max-width: 100%;
            max-height: 400px;
        }
    }

    .cropper-right {
        width: 200px;
        margin-left: 20px;

        .preview-title {
            text-align: center;
            font-weight: bold;
            margin-bottom: 10px;
        }

        .preview-image {
            img {
                max-width: 100%;
                max-height: 200px;
            }
        }
    }
}

.dialog-footer {
    text-align: center;

    .el-button {
        margin: 0 5px;
    }
}
:deep(.disabled) {
    .el-upload--picture-card,
    .el-upload-dragger {
        cursor: not-allowed;
        background: var(--el-disabled-bg-color) !important;
        border: 1px dashed var(--el-border-color-darker);
        &:hover {
            border-color: var(--el-border-color-darker) !important;
        }
    }
}
.upload-box {
    .no-border {
        :deep(.el-upload--picture-card) {
            border: none !important;
        }
    }
    :deep(.upload) {
        .el-upload-dragger {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
            padding: 0;
            overflow: hidden;
            border: 1px dashed var(--el-border-color-darker);
            border-radius: v-bind(borderRadius);
            &:hover {
                border: 1px dashed var(--el-color-primary);
            }
        }
        .el-upload-dragger.is-dragover {
            background-color: var(--el-color-primary-light-9);
            border: 2px dashed var(--el-color-primary) !important;
        }
        .el-upload-list__item,
        .el-upload--picture-card {
            width: v-bind(width);
            height: v-bind(height);
            background-color: transparent;
            border-radius: v-bind(borderRadius);
        }
        .upload-image {
            width: 100%;
            height: 100%;
            object-fit: contain;
        }
        .upload-handle {
            position: absolute;
            top: 0;
            right: 0;
            box-sizing: border-box;
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
            cursor: pointer;
            background: rgb(0 0 0 / 60%);
            opacity: 0;
            transition: var(--el-transition-duration-fast);
            .handle-icon {
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                padding: 0 6%;
                color: aliceblue;
                .el-icon {
                    margin-bottom: 15%;
                    font-size: 140%;
                }
                span {
                    font-size: 100%;
                }
            }
        }
        .el-upload-list__item {
            &:hover {
                .upload-handle {
                    opacity: 1;
                }
            }
        }
        .upload-empty {
            display: flex;
            flex-direction: column;
            align-items: center;
            font-size: 12px;
            line-height: 30px;
            color: var(--el-color-info);
            .el-icon {
                font-size: 28px;
                color: var(--el-text-color-secondary);
            }
        }
    }
    // 隐藏上传按钮的样式
    :deep(.hide-upload) {
        .el-upload--picture-card {
            display: none;
        }
    }
    .el-upload__tip {
        line-height: 15px;
        text-align: center;
    }
}
</style>
